﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition.Hosting;
using Toolbox.Configuration;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;

namespace Toolbox.Plugin.Configuration
{
    public class AddressableExportProvider : CatalogExportProvider
    {
        private readonly IConfigurationSource configurationSource;

        public AddressableExportProvider(ComposablePartCatalog catalog, bool isThreadSafe, IConfigurationSource configurationSource)
            : base(catalog, isThreadSafe)
        {
            // the configuration source determines where configuration values come from (eg. App.Config file)
            this.configurationSource = configurationSource;
        }

        public AddressableExportProvider(ComposablePartCatalog catalog, IConfigurationSource configurationSource)
            : base(catalog)
        {
            // the configuration source determines where configuration values come from (eg. App.Config file)
            this.configurationSource = configurationSource;
        }

        protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
        {
            var contractName = definition.ContractName;

            if (string.IsNullOrEmpty(contractName))
            {
                yield break;
            }

            if (definition.Cardinality != ImportCardinality.ZeroOrOne && definition.Cardinality != ImportCardinality.ExactlyOne)
            {
                yield break;
            }

            if (this.configurationSource.ContainsSetting(contractName))
            {
                Type targetType = null;
                string stringValue = "";

                if (ReflectionModelServices.IsImportingParameter(definition))
                {
                    var importingParameter = ReflectionModelServices.GetImportingParameter(definition);
                    targetType = importingParameter.Value.ParameterType;
                    stringValue = this.configurationSource.GetSetting(contractName);
                }
                else
                {
                    var getAccessor = ReflectionModelServices
                        .GetImportingMember(definition)
                        .GetAccessors()
                        .Where(x => x is MethodInfo)
                        .Select(x => x as MethodInfo)
                        .FirstOrDefault(x => (x.Attributes & MethodAttributes.SpecialName) == MethodAttributes.SpecialName && x.Name.StartsWith("get_", StringComparison.Ordinal));

                    if (getAccessor == null)
                    {
                        yield break;
                    }

                    targetType = getAccessor.ReturnType;

                    if (targetType == null || targetType.IsValueType)
                    {
                        yield break;
                    }

                    if (getAccessor.ReturnType.FullName.Equals(contractName))
                    {
                        stringValue = this.configurationSource.GetSetting(getAccessor.DeclaringType.FullName + "/" + getAccessor.Name.Remove(0, 4));
                    }
                    else
                    {
                        stringValue = this.configurationSource.GetSetting(contractName);
                    }


                    ImportDefinition newDefinition = new ImportDefinition(
                        exportDefinition =>
                            Test(exportDefinition, targetType),
                        targetType.FullName,
                        ImportCardinality.ZeroOrMore,
                        definition.IsRecomposable,
                        false);

                    IEnumerable<Export> exports = base.GetExportsCore(newDefinition, atomicComposition);

                    if (exports.Count() > 0)
                    {
                        yield return exports.First();
                    }
                }

            }
        }

        private bool Test(ExportDefinition exportDefinition, Type targetType)
        {
            return (exportDefinition.ContractName == targetType.FullName) && exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && targetType.FullName.Equals(exportDefinition.Metadata["ExportTypeIdentity"]);
        }
    }
}
